import { Clock, PerspectiveCamera, Scene, WebGLRenderer } from 'three';
import { OrbitControls } from 'three/examples/jsm/Addons.js';

export class TEngine {
  private static _instance: TEngine;

  public static get instance(): TEngine {
    if (!this._instance) {
      this._instance = new TEngine();
    }

    return this._instance;
  }

  public scene: Scene;
  public camera: PerspectiveCamera;
  public controls: OrbitControls;

  public container: HTMLDivElement;

  public renderer: WebGLRenderer;

  public resize: any;
  private _clock: Clock;

  constructor() {
    this.scene = new Scene();

    this.container = document.getElementById('app') as HTMLDivElement;

    this.camera = new PerspectiveCamera(
      45,
      this.container.clientWidth / this.container.clientHeight,
      0.1,
      1000,
    );

    this.renderer = new WebGLRenderer({
      antialias: true,
    });

    this.resize = this._resize.bind(this);
    this.container.appendChild(this.renderer.domElement);

    this.controls = new OrbitControls(this.camera, this.renderer.domElement);

    this._addEvent();

    this._clock = new Clock();

    this.renderer.setAnimationLoop(() => {
      const delta = this._clock.getDelta();
      this._updateEventList.forEach((item) => {
        item(delta);
      });
    });

    //添加渲染更新事件
    this.addUpdateEvent(() => {
      this.render();
    });
  }

  public render() {
    this.renderer.render(this.scene, this.camera);
  }

  private _updateEventList: any[] = [];

  public addUpdateEvent(updateCallback: Function) {
    this._updateEventList.push(updateCallback);
  }

  public removeUpdateEvent(updateCallback: Function) {
    this._updateEventList = this._updateEventList.filter((item) => item !== updateCallback);
  }

  //窗口自适应
  private _resize() {
    this.renderer.setSize(this.container.clientWidth, this.container.clientHeight);
    this.renderer.setPixelRatio(window.devicePixelRatio);
    this.camera.aspect = this.container.clientWidth / this.container.clientHeight;
    this.camera.updateProjectionMatrix();
  }

  private _addEvent() {
    window.addEventListener('resize', this.resize);
    this.resize();
  }

  private _removeEvent() {
    window.removeEventListener('resize', this.resize);
  }

  public destroy() {
    this._removeEvent();
    this.renderer.dispose();
    this._updateEventList = [];
  }
}
